Skip to content

feat(http): refactor node:http client instrumentation for portability#20393

Open
isaacs wants to merge 11 commits intodevelopfrom
isaacschlueter/portable-http-integration-client
Open

feat(http): refactor node:http client instrumentation for portability#20393
isaacs wants to merge 11 commits intodevelopfrom
isaacschlueter/portable-http-integration-client

Conversation

@isaacs
Copy link
Copy Markdown
Member

@isaacs isaacs commented Apr 20, 2026

Refactor the node:http outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
node:http module.

  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).
  • Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked.

Notes on test changes:

  • The test changes are mostly owing to the fact that more versions are covered by some of the conditions, so the test gets un-indented, because it was previously conditionalTest on a node version.
  • The origin span data changes from the vague (and now incorrect) auto.http.otel.http to auto.http.client for client spans. This also affects a lot of the tests.

Otherwise, all prior behavior should be unchanged, which is reflected in the integration and unit tests.

@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 1f9cef2 to 5ab332a Compare April 20, 2026 01:47
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 20, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 25.96 kB - -
@sentry/browser - with treeshaking flags 24.44 kB - -
@sentry/browser (incl. Tracing) 43.89 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 45.53 kB - -
@sentry/browser (incl. Tracing, Profiling) 48.84 kB - -
@sentry/browser (incl. Tracing, Replay) 83.09 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 72.59 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 87.77 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 100.38 kB - -
@sentry/browser (incl. Feedback) 43.18 kB - -
@sentry/browser (incl. sendFeedback) 30.77 kB - -
@sentry/browser (incl. FeedbackAsync) 35.93 kB - -
@sentry/browser (incl. Metrics) 27.25 kB - -
@sentry/browser (incl. Logs) 27.38 kB - -
@sentry/browser (incl. Metrics & Logs) 28.07 kB - -
@sentry/react 27.72 kB - -
@sentry/react (incl. Tracing) 46.13 kB - -
@sentry/vue 30.81 kB - -
@sentry/vue (incl. Tracing) 45.71 kB - -
@sentry/svelte 25.98 kB - -
CDN Bundle 28.65 kB -0.02% -4 B 🔽
CDN Bundle (incl. Tracing) 46.12 kB -0.01% -2 B 🔽
CDN Bundle (incl. Logs, Metrics) 30.03 kB -0.01% -2 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) 47.17 kB +0.01% +1 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 68.99 kB - -
CDN Bundle (incl. Tracing, Replay) 83.18 kB -0.01% -6 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 84.22 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 89 kB -0.01% -1 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 90.05 kB -0.01% -7 B 🔽
CDN Bundle - uncompressed 83.91 kB - -
CDN Bundle (incl. Tracing) - uncompressed 137.82 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 88.06 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 141.23 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 211.63 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 255.26 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 258.66 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 268.96 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 272.35 kB - -
@sentry/nextjs (client) 48.62 kB - -
@sentry/sveltekit (client) 44.33 kB - -
@sentry/node-core 59.09 kB +0.99% +578 B 🔺
@sentry/node 168.13 kB -4.38% -7.7 kB 🔽
@sentry/node - without tracing 72.03 kB -26.74% -26.29 kB 🔽
@sentry/aws-serverless 107.75 kB -6.73% -7.77 kB 🔽

View base workflow run

@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch 3 times, most recently from 18b93f6 to fd04ed5 Compare April 20, 2026 16:31
isaacs added a commit that referenced this pull request Apr 20, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 0877c6e to 1963717 Compare April 20, 2026 19:00
@isaacs isaacs marked this pull request as ready for review April 20, 2026 19:00
@isaacs isaacs requested review from andreiborza and mydea April 20, 2026 19:00
Comment thread packages/core/src/integrations/http/client-subscriptions.ts Outdated
isaacs added a commit that referenced this pull request Apr 20, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 1963717 to 3e7001c Compare April 20, 2026 19:06
Comment thread packages/core/src/integrations/http/client-subscriptions.ts Outdated
Comment thread packages/core/src/integrations/http/instrument-outgoing-request.ts Outdated
isaacs added a commit that referenced this pull request Apr 20, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 0307e3a to 640f8f0 Compare April 20, 2026 20:12
Comment thread packages/core/src/integrations/http/client-subscriptions.ts Outdated
Comment thread packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts Outdated
Comment thread packages/core/src/integrations/http/client-subscriptions.ts
Comment thread packages/node-core/src/light/integrations/httpIntegration.ts
Comment thread packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts Outdated
isaacs added a commit that referenced this pull request Apr 20, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 1b8377c to 1a8d51e Compare April 20, 2026 21:52
Comment thread packages/core/src/integrations/http/client-subscriptions.ts
Comment thread packages/node-core/src/utils/outgoingHttpRequest.ts
@isaacs isaacs requested a review from JPeer264 April 22, 2026 15:17
isaacs added a commit that referenced this pull request Apr 22, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
isaacs added a commit that referenced this pull request Apr 22, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
Copy link
Copy Markdown
Member

@JPeer264 JPeer264 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall it looks great. I just want to clarify the OTel double instrumentation before approving (it might not be an issue but I want to be sure)

Comment thread packages/core/src/integrations/http/client-subscriptions.ts
// if we do not have earlier access to the request object at creation
// time. The http.client.request.error channel is only available on
// the same node versions as client.request.created, so no help.
if (_options.breadcrumbs && !FULLY_SUPPORTS_HTTP_DIAGNOSTICS_CHANNEL) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: Do I understand it correctly, that http.client.request.error has been removed, because http.client.response.finish was here first, so http.client.request.error is kinda useless in our case, as finish did the trick already?!

If so, I wonder what would happen on newer Node versions where http.client.response.finish is not getting triggered - I think I'm missing something

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think you're actually noticing a valid point here. (It's very confusing!)

So, the flow is:

  • create ClientRequest object
  • fire http.client.request.created (node >=22.12 only)
  • pass object to user
  • user does things, calls .end() at some point.
  • Headers sent!
  • fire http.client.request.start (all node versions)
  • response arrives from server
  • user consumes response
  • fire http.client.response.finish (all node versions)

("All node versions" means >=18.6, so, all that we support anyway.)

If at some point, the request has an error, it'll fire http.client.request.error, but only on node >=22.6.0 || 20 >=20.17.0.

On node 22 >=22.12.0 || 23 >=23.2.0 || >=24 we have the http.client.request.created, and we attach an errorMonitor to the request object, so there's no need for http.client.request.error on those versions.

On nodes that don't support http.client.request.created, if breadcrumbs are enabled, we generate breadcrumbs on the http.client.response.finish. (This doesn't matter if we attached to request.created, because we do the errorMonitor and prependListener('response') listeners early enough.)

However, the valid point is that the response is not guaranteed, if the request has a socket hangup error or something before the response arrives.

I think the solution is that for old nodes, we attach a listener on the http.client.request.start diagnostics channel. This gets access to the request object, albeit too late to set headers, so no trace propagation. But it can attach the error listener to do breadcrumbs without a response. This covers more node versions than the http.client.request.error channel, and we don't need to have another version range to be checking.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On newer node versions, we're relying on the fact that the request.created channel listener attaches a response event handler, and then we deal with the response at that point.

* Inject Sentry trace-propagation headers into an outgoing request if the
* target URL matches the configured `tracePropagationTargets`.
*/
export function injectTracePropagationHeaders(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: There is already the _INTERNAL_getTracingHeadersForFetchRequest which is quite similar, with the difference that it doesn't apply the headers, but returns them. Theoretically we can try to reuse it, but not really mandatory for this PR.

@@ -0,0 +1,28 @@
import { objectToBaggageHeader, parseBaggageHeader } from '../../utils/baggage';

// TODO: should this be in utils/baggage?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Yeah good idea, it might make sense there, as these utilities there are quite similar. I would then suggest to move mergeBaggage there and maybe call it mergeBaggageHeader?!

export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => void) & { id: string })[] {
return [
instrumentSentryHttp,
instrumentOtelHttp,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q/l: Would that mean we can remove the @opentelemetry/instrumentation-http dependency as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, I had thought we needed that for the Server bits for now, but in fact we do not. Good catch!

export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => void) & { id: string })[] {
return [
instrumentSentryHttp,
instrumentOtelHttp,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q/m: Do you know what will happen for users that already have an OTel setup? Usually the HTTP instrumentation is on by default when doing auto instrumentation. Before it wouldn't do double instrumentation, as we already registered it, but now we removed this and OTel can now instrument it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point! Also, it's going to affect any instrumentations that we migrate from OTel to Sentry for use in other platforms, right?

This does add to the argument in favor of changing the span origin from auto.http.otel.http to auto.http.client (similarly for the express instrumentation). So, if users do start getting duplicate instrumentations, they'll know why.

I'll try to set up a demo app with this scenario, and see if there's a way we can detect the situation or somehow register with OTel in that case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good point! IMHO it is not a blocker if we can't get this to work. but maybe we can find a way to at least warn if we detect this somehow… but this may be hard due to timing, as we may be the first or last to wrap. ideal world, if possible:

  1. warn if we wrap and detect that otel has already wrapped it
  2. mark our wrapped stuff in a way that otel will also detect it as wrapped (??? not sure if this is feasible and/or has other problems attached to it…)

Copy link
Copy Markdown
Member

@JPeer264 JPeer264 Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it's going to affect any instrumentations that we migrate from OTel to Sentry for use in other platforms, right?

I think so yes.

This does add to the argument in favor of changing the span origin from auto.http.otel.http to auto.http.client (similarly for the express instrumentation). So, if users do start getting duplicate instrumentations, they'll know why.

Fair point. Maybe we can even set up a document somewhere where we basically have potential double instrumentation and would recommend to not do autoinstrumentation but allow list these instrumentations. Some "best practice" guide or something.

mark our wrapped stuff in a way that otel will also detect it as wrapped

Could work potentially - timing is an issue as you mentioned.

Comment thread packages/core/src/integrations/http/client-subscriptions.ts Outdated
import type { HttpClientRequest } from './types';

/**
* Inject Sentry trace-propagation headers into an outgoing request if the
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's mention here somewhere that this has to be called before the request is started, for future reference?

Copy link
Copy Markdown
Member

@mydea mydea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scanned it a bit and it generally looks sweet to me, great work! I'd ask @JPeer264 , @andreiborza and/or @nicohrubec to take some time to review this as it would be ideal to have broader understanding in the team about how these things work together, possibly worth it to do a session to sit together and explain the changes etc!

isaacs added a commit that referenced this pull request Apr 23, 2026
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from 8ad3dd0 to c5a2b94 Compare April 23, 2026 14:33
Comment thread packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts Outdated
isaacs added 3 commits April 24, 2026 10:44
This was implemented for the portable Express integration, but others
will need the same functionality, so make it a reusable util.
Refactor the `node:http` outgoing request instrumentation so that it can
be applied to non-Node.js environments by patching the http module.

Also, refactor so that the diagnostics_channel and monkeypatching paths
can share code, and so that light and normal node-core instrumentations
can share more of the functionality as well.

To facilitate this, some portable minimal types are vendored in from the
`node:http` module.
@isaacs isaacs force-pushed the isaacschlueter/portable-http-integration-client branch from c5a2b94 to 178c45e Compare April 24, 2026 23:40
Comment thread packages/node-core/src/light/integrations/httpIntegration.ts Outdated
Comment thread packages/node-core/src/integrations/http/SentryHttpInstrumentation.ts Outdated
Comment thread packages/core/src/integrations/http/client-patch.ts Outdated
Comment thread packages/core/src/utils/get-default-export.ts
Comment thread packages/core/src/integrations/http/get-request-url.ts
debug.error(LOG_PREFIX, 'Failed to set baggage header:', isError(e) ? e.message : 'Unknown error');
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trace propagation guard for existing sentry-trace removed

High Severity

The new injectTracePropagationHeaders checks for existing headers individually. When a sentry-trace header is already present, it still injects Sentry's traceparent and merges Sentry's DSC into baggage. This creates inconsistent trace propagation headers, which can break distributed tracing downstream.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c11ce7. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly the logic that was previously in packages/node-core/src/utils/outgoingHttpRequest.ts.

Comment thread packages/node-core/src/light/integrations/httpIntegration.ts
Comment thread packages/core/src/integrations/http/client-patch.ts
}

return objectToBaggageHeader(merged);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mergeBaggage reverses non-Sentry entry precedence

Medium Severity

The new mergeBaggage iterates the incoming baggage last and unconditionally writes every key, so non-Sentry entries from incoming overwrite identically-keyed non-Sentry entries already present on the request. The replaced mergeBaggageHeaders helper documented and implemented the opposite rule: non-Sentry entries from the existing header took precedence, and only Sentry-prefixed entries were overwritten. Outgoing baggage values that an application or middleware set on a request can now be silently replaced by current-scope values.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c11ce7. Configure here.

.start()
.completed();

closeTestServer();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Light http integration loses trace propagation on Node <22

Medium Severity

The node-core light httpIntegration only injects sentry-trace / baggage via the http.client.request.created diagnostic channel, which does not exist before Node 22.12. The fallback added for older Nodes (http.client.request.start) only attaches breadcrumb listeners — it cannot inject headers because that channel fires after headers have been sent. The corresponding integration test was un-gated from conditionalTest({ min: 22 }) and now unconditionally asserts sentry-trace and baggage are set on /api/v0 and /api/v1, which will fail when the suite runs against Node <22.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c11ce7. Configure here.

Comment thread packages/core/src/integrations/http/client-subscriptions.ts
injectTracePropagationHeaders(request, propagationDecisionMap);
}
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppressed tracing also disables breadcrumbs unconditionally

Low Severity

onHttpClientRequestCreated returns immediately when SUPPRESS_TRACING_KEY is set, so when a user wraps a call in Sentry.suppressTracing(...) no breadcrumb is recorded and no propagation is attempted — even when breadcrumbs: true and propagateTrace: false. Suppressing tracing semantically should suppress spans/propagation, not breadcrumbs. The previous diagnostics-channel paths for breadcrumbs (http.client.response.finish and http.client.request.error) were not gated on this key.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9abf370. Configure here.

const protocol = request.protocol ?? 'http:';
const path = request.path ?? '/';
return `${protocol}//${hostname}${path}`;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outgoing request URL no longer normalized

Medium Severity

getRequestUrl builds the URL by simple string concatenation, whereas the previous getBreadcrumbData and similar helpers passed the constructed URL through new URL(...), which normalized things like default-port stripping and path resolution. As a result, breadcrumb url, http.url, and tracePropagationTargets matching can now see values like http://example.com:80/ where they previously saw http://example.com/, and any user-configured tracePropagationTargets regex that assumed the normalized form may no longer match. This can also flow through to getSanitizedUrlString(parseUrl(url)), where unparsed concatenation is more likely to fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9abf370. Configure here.

onOutgoingResponseFinish(request, response, _options);
});
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breadcrumb timing changes on older Node versions

Low Severity

The fallback path for Node versions without http.client.request.created now subscribes to http.client.request.start and emits the outgoing request breadcrumb synchronously inside the 'response' listener — i.e. when the response starts, not when it ends. The old light-mode code used http.client.response.finish, which fires after the response body fully arrives. As a result, on older Nodes the breadcrumb's relative ordering versus subsequent events shifts, and the breadcrumb is recorded before the response is actually complete.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9abf370. Configure here.

return request;
});
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patched http.get drops options object handling

Medium Severity

The replacement patchedGet calls httpModule.request.apply(this, args) and then request.end(). Node's real http.get performs additional argument normalization (e.g. forwarding the URL/options/callback triple correctly and handling agent/Agent defaults) before delegating to request. Although in current Node get is essentially request(...) ; req.end(), replacing it wholesale (rather than wrapping the original) discards any future or platform-specific behavior baked into the original get, including handling of options.agent === false semantics that some custom HTTP modules implement.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9abf370. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node's http.get just does:

function get(url, options, cb) {
  const req = request(url, options, cb);
  req.end();
  return req;
}

So there really isn't any argument normalization or anything.

}
response.on('end', () => addOutgoingRequestBreadcrumb(request, response));
response.on(errorMonitor, () => addOutgoingRequestBreadcrumb(request, undefined));
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response error breadcrumb omits response in non-span path

Low Severity

In the no-spans branch, the response errorMonitor listener calls addOutgoingRequestBreadcrumb(request, undefined), dropping the available response object. This means breadcrumbs for partially-received responses lose their status_code and log level even though the response was reached. The corresponding span-enabled branch passes response to the breadcrumb in finishWithResponse, so the two paths emit breadcrumbs with different fidelity for the same scenario.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ef6e736. Configure here.

Comment thread packages/core/src/integrations/http/client-patch.ts
Comment on lines +70 to +76
request.on(errorMonitor, () => addBreadcrumbs(request, undefined));
request.prependListener('response', response => {
if (request.listenerCount('response') <= 1) {
response.resume();
}
response.on('end', () => addBreadcrumbs(request, response));
response.on(errorMonitor, () => addBreadcrumbs(request, undefined));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: An early return in onHttpClientRequestCreated causes a ReferenceError when accessing addedBreadcrumbs in async event listeners because it's declared too late.
Severity: CRITICAL

Suggested Fix

Move the declaration let addedBreadcrumbs = false; from line 110 to the top of the onHttpClientRequestCreated function, before the if (!createSpans) block. This will ensure the variable is always initialized before any closures that use it are created.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/core/src/integrations/http/client-subscriptions.ts#L70-L76

Potential issue: In the `onHttpClientRequestCreated` function, when `createSpans` is
false, event listeners are registered that will later call the `addBreadcrumbs`
function. However, an early `return` is executed before the `addedBreadcrumbs` variable
is declared and initialized. Due to JavaScript's Temporal Dead Zone (TDZ) for `let`
declarations, when the asynchronous event listeners fire, the call to `addBreadcrumbs`
attempts to access the uninitialized `addedBreadcrumbs` variable. This results in a
`ReferenceError`, which will crash any outgoing HTTP request made when using the light
integration or when spans are otherwise disabled.

Also affects:

  • packages/core/src/integrations/http/client-subscriptions.ts:110~116

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 9 total unresolved issues (including 8 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 645dc0e. Configure here.

injectTracePropagationHeaders(request, propagationDecisionMap);
}
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TDZ ReferenceError in no-spans breadcrumb path

High Severity

When createSpans is false, the addedBreadcrumbs variable is not initialized because its declaration is after an early return. This causes a ReferenceError when event listeners call addBreadcrumbs, preventing outgoing request breadcrumbs from being added when spans are disabled (e.g., light mode or tracing off).

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 645dc0e. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants